package vga

import (
    "unsafe"
    "go-os-qemu/pkg/portio"
)

const (
    textBuffer = 0xB8000
    width      = 80
    height     = 25
    crtcIndex  = 0x3D4
    crtcData   = 0x3D5
)

// Foreground/background color indices (VGA text mode)
const (
    ColorBlack        = 0x0
    ColorBlue         = 0x1
    ColorGreen        = 0x2
    ColorCyan         = 0x3
    ColorRed          = 0x4
    ColorMagenta      = 0x5
    ColorBrown        = 0x6
    ColorLightGray    = 0x7
    ColorDarkGray     = 0x8
    ColorLightBlue    = 0x9
    ColorLightGreen   = 0xA
    ColorLightCyan    = 0xB
    ColorLightRed     = 0xC
    ColorLightMagenta = 0xD
    ColorYellow       = 0xE
    ColorWhite        = 0xF
)

var (
    buffer   = (*[width * height]uint16)(unsafe.Pointer(uintptr(textBuffer)))
    cursorX  = 0
    cursorY  = 0
    attrByte byte = (ColorBlack<<4 | ColorWhite) // default: white on black
)

// SetColor sets the current foreground/background color.
func SetColor(fg, bg byte) {
    attrByte = (bg<<4 | (fg & 0x0F))
}

// SetCursor sets the current cursor position.
func SetCursor(x, y int) {
    if x < 0 || x >= width || y < 0 || y >= height {
        return
    }
    cursorX, cursorY = x, y
    updateCursorHardware()
}

// Clear wipes the entire VGA text buffer and resets the cursor.
func Clear() {
    fill := uint16(attrByte)<<8 | uint16(' ')
    for i := range buffer {
        buffer[i] = fill
    }
    cursorX, cursorY = 0, 0
    updateCursorHardware()
}

// writeChar writes a character at the current cursor and advances it.
func writeChar(r rune) {
    // Handle backspace: erase previous character
    if r == '\b' {
        if cursorX > 0 {
            cursorX--
            idx := cursorY*width + cursorX
            if idx >= 0 && idx < len(buffer) {
                buffer[idx] = uint16(attrByte)<<8 | uint16(' ')
            }
        }
        updateCursorHardware()
        return
    }
    if r == '\n' {
        newline()
        return
    }
    if cursorX >= width {
        newline()
    }
    if cursorY >= height {
        scroll()
    }
    idx := cursorY*width + cursorX
    if idx >= len(buffer) {
        return
    }
    buffer[idx] = uint16(attrByte)<<8 | uint16(r)
    cursorX++
    updateCursorHardware()
}

// newline moves the cursor to the start of the next line.
func newline() {
    cursorX = 0
    cursorY++
    if cursorY >= height {
        scroll()
    }
    updateCursorHardware()
}

// scroll shifts the screen up by one row.
func scroll() {
    // Move lines up by one row
    for i := width; i < width*height; i++ {
        buffer[i-width] = buffer[i]
    }
    // Clear last line
    fill := uint16(attrByte)<<8 | uint16(' ')
    base := (height - 1) * width
    for i := 0; i < width; i++ {
        buffer[base+i] = fill
    }
    cursorY = height - 1
    cursorX = 0
    updateCursorHardware()
}

// Print writes a string at the current cursor, handling newlines and scrolling.
func Print(s string) {
    for _, r := range s {
        writeChar(r)
    }
}

// Println writes a string and then a newline.
func Println(s string) {
    Print(s)
    newline()
}

// PrintAt writes the provided string at the given coordinates without changing the cursor.
func PrintAt(x, y int, s string) {
    if x < 0 || x >= width || y < 0 || y >= height {
        return
    }
    base := y*width + x
    for i, r := range s {
        if base+i >= len(buffer) {
            break
        }
        buffer[base+i] = uint16(attrByte)<<8 | uint16(r)
    }
}

// updateCursorHardware syncs VGA hardware cursor to (cursorX, cursorY).
func updateCursorHardware() {
    pos := cursorY*width + cursorX
    // low byte
    portio.Outb(crtcIndex, 0x0F)
    portio.Outb(crtcData, byte(pos&0xFF))
    // high byte
    portio.Outb(crtcIndex, 0x0E)
    portio.Outb(crtcData, byte((pos>>8)&0xFF))
}
